package models

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cy77cc/hioshop/global"
	"github.com/redis/go-redis/v9"
	"gorm.io/gorm"
	"math/rand"
	"time"
)

type Goods struct {
	Model
	CategoryId        uint   `json:"category_id" gorm:"default:0"`
	IsOnSale          uint8  `json:"is_on_sale" gorm:"default:1"`
	Name              string `json:"name"`
	GoodsNumber       uint   `json:"goods_number"`
	SellVolume        uint   `json:"sell_volume"`
	Keywords          string `json:"keywords"`
	RetailPrice       string `json:"retail_price"`
	MinRetailPrice    string `json:"min_retail_price"`
	CostPrice         string `json:"cost_price"`
	MinCostPrice      string `json:"min_cost_price"`
	GoodsBrief        string `json:"goods_brief"`
	GoodsDesc         string `json:"goods_desc" gorm:"type:text"`
	SortOrder         uint   `json:"sort_order"`
	IsIndex           int8   `json:"is_index"`
	IsNew             int8   `json:"is_new"`
	GoodsUnit         string `json:"goods_unit"`
	HttpsPicUrl       string `json:"https_pic_url"`
	ListPicUrl        string `json:"list_pic_url"`
	FreightTemplateId int    `json:"freight_template_id"`
	FreightType       int    `json:"freight_type"`
	IsDelete          uint8  `json:"is_delete"`
	HasGallery        int8   `json:"has_gallery"`
	HasDone           int8   `json:"has_done"`
}

func (g *Goods) GetGoodsByCategoryId(id uint, page int, pageSize int) (list []Goods, err error) {

	ctx := context.Background()
	var lRange *redis.StringSliceCmd
	if page == 0 && pageSize == 0 {
		lRange = global.REDIS.LRange(ctx, fmt.Sprintf("category:%d", id), 0, -1)
	} else {
		lRange = global.REDIS.LRange(ctx, fmt.Sprintf("category:%d", id), int64((page-1)*pageSize), int64(page*pageSize))
	}
	if lRange.Err() != nil || len(lRange.Val()) == 0 {
		query := global.DB.Model(&Goods{}).Where(&Goods{
			CategoryId: id,
			IsOnSale:   1,
			IsIndex:    1,
			IsDelete:   0,
		}).Where("goods_number>=0").Order("sort_order asc")
		err = query.Find(&list).Error

		// 如果这些信息不存在，防止缓存穿透
		if errors.Is(err, gorm.ErrRecordNotFound) {
			global.REDIS.RPush(ctx, fmt.Sprintf("category:%d", id), "")
			return
		}

		for _, v := range list {
			data, _ := json.Marshal(v)
			global.REDIS.RPush(ctx, fmt.Sprintf("category:%d", id), data)
		}

		if page != 0 && pageSize != 0 {
			query = query.Offset((page - 1) * pageSize).Limit(pageSize)
		}
		err = query.Find(&list).Error

		rint := time.Duration(rand.Intn(50))
		global.REDIS.Expire(ctx, fmt.Sprintf("category:%d", id), time.Hour+rint)
		return
	}

	for _, v := range lRange.Val() {
		goods := Goods{}
		_ = json.Unmarshal([]byte(v), &goods)
		list = append(list, goods)
	}

	return
}

func (g *Goods) GetGoodsAll(page int, pageSize int, expr map[string]any) (list []Goods, err error) {
	query := global.DB.Model(&Goods{}).Where(expr).Where("goods_number>=0").Order("sort_order asc")
	if page != 0 && pageSize != 0 {
		query = query.Offset((page - 1) * pageSize).Limit(pageSize)
	}
	err = query.Find(&list).Error
	return
}

func (g *Goods) GetDetail() error {
	return global.DB.Model(g).Where(map[string]any{
		"id":        g.ID,
		"is_delete": 0,
	}).Find(g).Error
}

func (g *Goods) GetGoodsByKeyword(keyword string, order string) ([]Goods, error) {
	list := make([]Goods, 0, 16)
	err := global.DB.Model(&Goods{}).Where(map[string]any{
		"is_on_sale": 1,
		"is_delete":  0,
	}).Where("name like concat('%',?,'%')", keyword).Order(order).Find(&list).Error
	return list, err
}

func (*Goods) GetGoodsNum(expr map[string]any) (num int64, err error) {
	err = global.DB.Model(&Goods{}).Where(expr).Where("is_delete=0").Count(&num).Error
	return
}

type GoodsGallery struct {
	Model
	GoodsId   uint   `json:"goods_id"`
	ImgUrl    string `json:"img_url"`
	ImgDesc   string `json:"img_desc"`
	SortOrder uint   `json:"sort_order"`
	IsDelete  int8   `json:"is_delete"`
}

func (g *GoodsGallery) GetGoodsGalleryByGoodsId(id uint) (list []GoodsGallery, err error) {
	err = global.DB.Model(&GoodsGallery{}).Where(map[string]any{
		"goods_id":  id,
		"is_delete": 0,
	}).Order("sort_order").Limit(6).Find(&list).Error
	return
}

type GoodsSpecification struct {
	Model
	GoodsId         uint   `json:"goods_id"`
	SpecificationId uint   `json:"specification_id"`
	Value           string `json:"value"`
	PicUrl          string `json:"pic_url"`
	IsDelete        int8   `json:"is_delete"`
}

func (GoodsSpecification) GetGoodsSpecificationById(id uint) (list []GoodsSpecification, err error) {
	err = global.DB.Model(&GoodsSpecification{}).Where(&GoodsSpecification{GoodsId: id, IsDelete: 0}).Find(&list).Error
	return
}
